import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import cv2
import numpy as np
import json
import pandas as pd
import os
from PIL import Image, ImageTk
import threading
import time
from datetime import datetime

class VideoProcessor:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("视频数字识别工具")
        self.root.geometry("1200x800")
        
        # 视频相关变量
        self.video_path = None
        self.cap = None
        self.current_frame = None
        self.frame_rate = 30
        self.is_playing = False
        self.current_frame_index = 0
        
        # 矩形框相关变量
        self.rectangles = []
        self.selected_rect = None
        self.drawing = False
        self.start_x = 0
        self.start_y = 0
        
        # 矩形框调整大小和移动相关变量
        self.resizing = False
        self.resize_handle = None  # 调整大小的手柄位置
        self.resize_start_x = 0
        self.resize_start_y = 0
        
        # 矩形框移动相关变量
        self.moving = False
        self.move_start_x = 0
        self.move_start_y = 0
        self.move_start_rect_coords = None
        
        # 识别结果
        self.recognition_results = []
        
        # 识别控制
        self.is_recognizing = False
        
        self.setup_ui()
        self.load_config()
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 左侧控制面板
        control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding=10)
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        
        # 文件选择
        ttk.Button(control_frame, text="选择视频文件", command=self.select_video).pack(fill=tk.X, pady=5)
        
        # 视频信息
        self.info_label = ttk.Label(control_frame, text="未选择视频文件")
        self.info_label.pack(pady=5)
        
        # 播放控制
        play_frame = ttk.Frame(control_frame)
        play_frame.pack(fill=tk.X, pady=10)
        
        ttk.Button(play_frame, text="播放", command=self.play_video).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(play_frame, text="暂停", command=self.pause_video).pack(side=tk.LEFT)
        
        # 矩形框管理
        rect_frame = ttk.LabelFrame(control_frame, text="矩形框管理", padding=10)
        rect_frame.pack(fill=tk.X, pady=10)
        
        ttk.Button(rect_frame, text="添加矩形框", command=self.add_rectangle).pack(fill=tk.X, pady=2)
        ttk.Button(rect_frame, text="删除选中框", command=self.delete_selected_rect).pack(fill=tk.X, pady=2)
        ttk.Button(rect_frame, text="清空所有框", command=self.clear_all_rectangles).pack(fill=tk.X, pady=2)
        
        # 备注名称输入
        ttk.Label(rect_frame, text="备注名称:").pack(anchor=tk.W, pady=(10, 0))
        self.name_entry = ttk.Entry(rect_frame)
        self.name_entry.pack(fill=tk.X, pady=2)
        ttk.Button(rect_frame, text="设置名称", command=self.set_rectangle_name).pack(fill=tk.X, pady=2)
        
        # 识别和导出
        action_frame = ttk.LabelFrame(control_frame, text="操作", padding=10)
        action_frame.pack(fill=tk.X, pady=10)
        
        ttk.Button(action_frame, text="开始识别", command=self.start_recognition).pack(fill=tk.X, pady=2)
        ttk.Button(action_frame, text="停止识别", command=self.stop_recognition).pack(fill=tk.X, pady=2)
        ttk.Button(action_frame, text="导出CSV", command=self.export_csv).pack(fill=tk.X, pady=2)
        ttk.Button(action_frame, text="保存配置", command=self.save_config).pack(fill=tk.X, pady=2)
        ttk.Button(action_frame, text="载入配置", command=self.load_config_dialog).pack(fill=tk.X, pady=2)
        
        # 右侧视频显示区域
        video_frame = ttk.LabelFrame(main_frame, text="视频显示", padding=10)
        video_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 视频画布
        self.canvas = tk.Canvas(video_frame, bg="black")
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        # 绑定鼠标事件
        self.canvas.bind("<Button-1>", self.on_mouse_down)
        self.canvas.bind("<B1-Motion>", self.on_mouse_drag)
        self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up)
        self.canvas.bind("<Button-3>", self.on_right_click)
        self.canvas.bind("<Double-Button-1>", self.on_double_click)
        
        # 状态栏
        self.status_label = ttk.Label(self.root, text="就绪", relief=tk.SUNKEN)
        self.status_label.pack(side=tk.BOTTOM, fill=tk.X)
        
    def select_video(self):
        file_path = filedialog.askopenfilename(
            title="选择视频文件",
            filetypes=[("视频文件", "*.mp4 *.avi *.mov *.mkv"), ("所有文件", "*.*")]
        )
        
        if file_path:
            self.video_path = file_path
            self.load_video()
            
    def load_video(self):
        if self.cap:
            self.cap.release()
            
        self.cap = cv2.VideoCapture(self.video_path)
        if not self.cap.isOpened():
            messagebox.showerror("错误", "无法打开视频文件")
            return
            
        self.frame_rate = int(self.cap.get(cv2.CAP_PROP_FPS))
        total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
        duration = total_frames / self.frame_rate
        
        self.info_label.config(text=f"帧率: {self.frame_rate} FPS\n总帧数: {total_frames}\n时长: {duration:.2f}秒")
        
        # 自动读取并显示第一帧
        ret, frame = self.cap.read()
        if ret:
            self.current_frame = frame
            self.display_frame()
            # 重置到第一帧，方便后续识别
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            
    def display_frame(self):
        if self.current_frame is None:
            return
            
        # 调整帧大小以适应画布
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        
        if canvas_width > 1 and canvas_height > 1:
            frame_height, frame_width = self.current_frame.shape[:2]
            
            # 计算缩放比例
            scale_x = canvas_width / frame_width
            scale_y = canvas_height / frame_height
            scale = min(scale_x, scale_y)
            
            new_width = int(frame_width * scale)
            new_height = int(frame_height * scale)
            
            # 缩放帧
            resized_frame = cv2.resize(self.current_frame, (new_width, new_height))
            
            # 转换为PIL图像
            rgb_frame = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2RGB)
            pil_image = Image.fromarray(rgb_frame)
            self.photo = ImageTk.PhotoImage(pil_image)
            
            # 清除画布并显示图像
            self.canvas.delete("all")
            self.canvas.create_image(
                canvas_width // 2, canvas_height // 2,
                image=self.photo, anchor=tk.CENTER
            )
            
            # 绘制矩形框
            self.draw_rectangles(scale)
            
    def draw_rectangles(self, scale=1.0):
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        
        for i, rect in enumerate(self.rectangles):
            x1, y1, x2, y2, name = rect
            
            # 缩放坐标
            scaled_x1 = int(x1 * scale) + (canvas_width - int(self.current_frame.shape[1] * scale)) // 2
            scaled_y1 = int(y1 * scale) + (canvas_height - int(self.current_frame.shape[0] * scale)) // 2
            scaled_x2 = int(x2 * scale) + (canvas_width - int(self.current_frame.shape[1] * scale)) // 2
            scaled_y2 = int(y2 * scale) + (canvas_height - int(self.current_frame.shape[0] * scale)) // 2
            
            # 绘制矩形
            color = "red" if i == self.selected_rect else "green"
            self.canvas.create_rectangle(
                scaled_x1, scaled_y1, scaled_x2, scaled_y2,
                outline=color, width=2
            )
            
            # 绘制名称标签
            if name:
                self.canvas.create_text(
                    scaled_x1, scaled_y1 - 20,
                    text=name, anchor=tk.SW,
                    fill=color, font=("Arial", 10, "bold")
                )
            
            # 如果被选中，绘制调整大小的手柄
            if i == self.selected_rect:
                # 绘制四个角的手柄
                handle_size = 6
                handles = [
                    (scaled_x1, scaled_y1),  # 左上角
                    (scaled_x2, scaled_y1),  # 右上角
                    (scaled_x2, scaled_y2),  # 右下角
                    (scaled_x1, scaled_y2)   # 左下角
                ]
                
                for handle_x, handle_y in handles:
                    self.canvas.create_rectangle(
                        handle_x - handle_size//2, handle_y - handle_size//2,
                        handle_x + handle_size//2, handle_y + handle_size//2,
                        fill="white", outline="red", width=1
                    )
                
    def on_mouse_down(self, event):
        # 检查是否点击了调整大小的手柄
        if self.selected_rect is not None:
            handle_clicked = self.check_handle_click(event.x, event.y)
            if handle_clicked:
                self.resizing = True
                self.resize_handle = handle_clicked
                self.resize_start_x = event.x
                self.resize_start_y = event.y
                return
            
            # 检查是否点击了矩形框内部（用于移动）
            rect_clicked = self.check_rect_click(event.x, event.y)
            if rect_clicked == self.selected_rect:
                self.moving = True
                self.move_start_x = event.x
                self.move_start_y = event.y
                # 保存当前矩形框坐标
                x1, y1, x2, y2, name = self.rectangles[self.selected_rect]
                self.move_start_rect_coords = [x1, y1, x2, y2, name]
                return
        
        # 检查是否点击了其他矩形框
        rect_clicked = self.check_rect_click(event.x, event.y)
        if rect_clicked is not None:
            self.selected_rect = rect_clicked
            self.display_frame()
            return
        
        # 开始绘制新矩形框
        self.drawing = True
        self.start_x = event.x
        self.start_y = event.y
        
    def on_mouse_drag(self, event):
        if self.resizing:
            # 调整矩形框大小
            self.resize_rectangle(event.x, event.y)
        elif self.moving:
            # 移动矩形框
            self.move_rectangle(event.x, event.y)
        elif self.drawing:
            self.canvas.delete("temp_rect")
            self.canvas.create_rectangle(
                self.start_x, self.start_y, event.x, event.y,
                outline="yellow", width=2, tags="temp_rect"
            )
            
    def on_mouse_up(self, event):
        if self.resizing:
            self.resizing = False
            self.resize_handle = None
        elif self.moving:
            self.moving = False
            self.move_start_rect_coords = None
        elif self.drawing:
            self.drawing = False
            self.canvas.delete("temp_rect")
            
            # 计算实际坐标（相对于视频帧）
            canvas_width = self.canvas.winfo_width()
            canvas_height = self.canvas.winfo_height()
            
            frame_height, frame_width = self.current_frame.shape[:2]
            scale_x = canvas_width / frame_width
            scale_y = canvas_height / frame_height
            scale = min(scale_x, scale_y)
            
            # 计算视频帧在画布中的偏移
            offset_x = (canvas_width - int(frame_width * scale)) // 2
            offset_y = (canvas_height - int(frame_height * scale)) // 2
            
            # 转换坐标
            x1 = (self.start_x - offset_x) / scale
            y1 = (self.start_y - offset_y) / scale
            x2 = (event.x - offset_x) / scale
            y2 = (event.y - offset_y) / scale
            
            # 确保坐标在有效范围内
            x1 = max(0, min(x1, frame_width))
            y1 = max(0, min(y1, frame_height))
            x2 = max(0, min(x2, frame_width))
            y2 = max(0, min(y2, frame_height))
            
            # 添加矩形框
            self.rectangles.append([x1, y1, x2, y2, ""])
            self.selected_rect = len(self.rectangles) - 1
            self.display_frame()
            
    def on_right_click(self, event):
        # 右键选择矩形框
        rect_clicked = self.check_rect_click(event.x, event.y)
        if rect_clicked is not None:
            self.selected_rect = rect_clicked
            self.display_frame()
    
    def on_double_click(self, event):
        # 双击编辑矩形框名称
        rect_clicked = self.check_rect_click(event.x, event.y)
        if rect_clicked is not None:
            self.selected_rect = rect_clicked
            self.edit_rectangle_name()
    
    def check_rect_click(self, click_x, click_y):
        """检查点击位置是否在矩形框内"""
        if self.current_frame is None:
            return None
            
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        
        frame_height, frame_width = self.current_frame.shape[:2]
        scale_x = canvas_width / frame_width
        scale_y = canvas_height / frame_height
        scale = min(scale_x, scale_y)
        
        offset_x = (canvas_width - int(frame_width * scale)) // 2
        offset_y = (canvas_height - int(frame_height * scale)) // 2
        
        # 转换点击坐标到视频帧坐标系
        frame_x = (click_x - offset_x) / scale
        frame_y = (click_y - offset_y) / scale
        
        # 检查是否在某个矩形框内
        for i, rect in enumerate(self.rectangles):
            x1, y1, x2, y2, _ = rect
            if x1 <= frame_x <= x2 and y1 <= frame_y <= y2:
                return i
        return None
    
    def check_handle_click(self, click_x, click_y):
        """检查是否点击了调整大小的手柄"""
        if self.selected_rect is None or self.current_frame is None:
            return None
            
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        
        frame_height, frame_width = self.current_frame.shape[:2]
        scale_x = canvas_width / frame_width
        scale_y = canvas_height / frame_height
        scale = min(scale_x, scale_y)
        
        offset_x = (canvas_width - int(frame_width * scale)) // 2
        offset_y = (canvas_height - int(frame_height * scale)) // 2
        
        # 获取选中矩形框的坐标
        x1, y1, x2, y2, _ = self.rectangles[self.selected_rect]
        
        # 转换为画布坐标
        canvas_x1 = int(x1 * scale) + offset_x
        canvas_y1 = int(y1 * scale) + offset_y
        canvas_x2 = int(x2 * scale) + offset_x
        canvas_y2 = int(y2 * scale) + offset_y
        
        # 手柄大小
        handle_size = 6
        
        # 检查四个角的手柄
        handles = [
            (canvas_x1, canvas_y1, "top_left"),
            (canvas_x2, canvas_y1, "top_right"),
            (canvas_x2, canvas_y2, "bottom_right"),
            (canvas_x1, canvas_y2, "bottom_left")
        ]
        
        for handle_x, handle_y, handle_type in handles:
            if (handle_x - handle_size//2 <= click_x <= handle_x + handle_size//2 and
                handle_y - handle_size//2 <= click_y <= handle_y + handle_size//2):
                return handle_type
        
        return None
    
    def resize_rectangle(self, new_x, new_y):
        """调整矩形框大小"""
        if self.selected_rect is None or self.resize_handle is None:
            return
            
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        
        frame_height, frame_width = self.current_frame.shape[:2]
        scale_x = canvas_width / frame_width
        scale_y = canvas_height / frame_height
        scale = min(scale_x, scale_y)
        
        offset_x = (canvas_width - int(frame_width * scale)) // 2
        offset_y = (canvas_height - int(frame_height * scale)) // 2
        
        # 转换新坐标到视频帧坐标系
        frame_x = (new_x - offset_x) / scale
        frame_y = (new_y - offset_y) / scale
        
        # 确保坐标在有效范围内
        frame_x = max(0, min(frame_x, frame_width))
        frame_y = max(0, min(frame_y, frame_height))
        
        # 获取当前矩形框坐标
        x1, y1, x2, y2, name = self.rectangles[self.selected_rect]
        
        # 根据手柄位置调整坐标
        if self.resize_handle == "top_left":
            x1, y1 = frame_x, frame_y
        elif self.resize_handle == "top_right":
            x2, y1 = frame_x, frame_y
        elif self.resize_handle == "bottom_right":
            x2, y2 = frame_x, frame_y
        elif self.resize_handle == "bottom_left":
            x1, y2 = frame_x, frame_y
        
        # 确保矩形框有效（左上角在右下角左边和上边）
        x1, x2 = min(x1, x2), max(x1, x2)
        y1, y2 = min(y1, y2), max(y1, y2)
        
        # 更新矩形框坐标
        self.rectangles[self.selected_rect] = [x1, y1, x2, y2, name]
        
        # 刷新显示
        self.display_frame()
    
    def edit_rectangle_name(self):
        """编辑矩形框名称"""
        if self.selected_rect is None:
            return
            
        # 创建编辑对话框
        dialog = tk.Toplevel(self.root)
        dialog.title("编辑矩形框名称")
        dialog.geometry("300x150")
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 居中显示
        dialog.geometry("+%d+%d" % (self.root.winfo_rootx() + 50, self.root.winfo_rooty() + 50))
        
        # 输入框
        ttk.Label(dialog, text="请输入新的名称:").pack(pady=10)
        name_entry = ttk.Entry(dialog, width=30)
        name_entry.pack(pady=10)
        name_entry.insert(0, self.rectangles[self.selected_rect][4])
        name_entry.select_range(0, tk.END)
        name_entry.focus()
        
        # 按钮
        button_frame = ttk.Frame(dialog)
        button_frame.pack(pady=20)
        
        def save_name():
            new_name = name_entry.get().strip()
            if new_name:
                self.rectangles[self.selected_rect][4] = new_name
                self.display_frame()
            dialog.destroy()
        
        def cancel():
            dialog.destroy()
        
        ttk.Button(button_frame, text="保存", command=save_name).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=cancel).pack(side=tk.LEFT, padx=5)
        
                # 绑定回车键
        name_entry.bind("<Return>", lambda e: save_name())
        name_entry.bind("<Escape>", lambda e: cancel())
    
    def move_rectangle(self, new_x, new_y):
        """移动矩形框"""
        if self.selected_rect is None or self.move_start_rect_coords is None:
            return
            
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        
        frame_height, frame_width = self.current_frame.shape[:2]
        scale_x = canvas_width / frame_width
        scale_y = canvas_height / frame_height
        scale = min(scale_x, scale_y)
        
        offset_x = (canvas_width - int(frame_width * scale)) // 2
        offset_y = (canvas_height - int(frame_height * scale)) // 2
        
        # 计算移动距离（画布坐标）
        delta_x = new_x - self.move_start_x
        delta_y = new_y - self.move_start_y
        
        # 转换为视频帧坐标系
        delta_frame_x = delta_x / scale
        delta_frame_y = delta_y / scale
        
        # 获取原始坐标
        x1, y1, x2, y2, name = self.move_start_rect_coords
        
        # 计算新坐标
        new_x1 = x1 + delta_frame_x
        new_y1 = y1 + delta_frame_y
        new_x2 = x2 + delta_frame_x
        new_y2 = y2 + delta_frame_y
        
        # 确保矩形框不超出视频边界
        new_x1 = max(0, min(new_x1, frame_width))
        new_y1 = max(0, min(new_y1, frame_height))
        new_x2 = max(0, min(new_x2, frame_width))
        new_y2 = max(0, min(new_y2, frame_height))
        
        # 确保矩形框有效
        new_x1, new_x2 = min(new_x1, new_x2), max(new_x1, new_x2)
        new_y1, new_y2 = min(new_y1, new_y2), max(new_y1, new_y2)
        
        # 更新矩形框坐标
        self.rectangles[self.selected_rect] = [new_x1, new_y1, new_x2, new_y2, name]
        
        # 刷新显示
        self.display_frame()
    
    def add_rectangle(self):
        if self.current_frame is None:
            messagebox.showwarning("警告", "请先选择视频文件")
            return
            
        # 添加默认矩形框
        frame_height, frame_width = self.current_frame.shape[:2]
        x1 = frame_width // 4
        y1 = frame_height // 4
        x2 = 3 * frame_width // 4
        y2 = 3 * frame_height // 4
        
        self.rectangles.append([x1, y1, x2, y2, ""])
        self.selected_rect = len(self.rectangles) - 1
        self.display_frame()
        
    def delete_selected_rect(self):
        if self.selected_rect is not None and 0 <= self.selected_rect < len(self.rectangles):
            del self.rectangles[self.selected_rect]
            self.selected_rect = None
            self.display_frame()
            
    def clear_all_rectangles(self):
        self.rectangles.clear()
        self.selected_rect = None
        self.display_frame()
        
    def set_rectangle_name(self):
        if self.selected_rect is not None and 0 <= self.selected_rect < len(self.rectangles):
            name = self.name_entry.get().strip()
            if name:
                self.rectangles[self.selected_rect][4] = name
                self.display_frame()
                self.name_entry.delete(0, tk.END)
                
    def play_video(self):
        if not self.cap or self.is_playing:
            return
            
        self.is_playing = True
        threading.Thread(target=self.video_loop, daemon=True).start()
        
    def pause_video(self):
        self.is_playing = False
        
    def video_loop(self):
        frame_count = 0
        
        while self.is_playing and self.cap:
            ret, frame = self.cap.read()
            if not ret:
                self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
                continue
                
            self.current_frame = frame
            self.current_frame_index = frame_count
            
            # 根据帧率控制播放速度
            time.sleep(1.0 / self.frame_rate)
            
            # 在主线程中更新UI
            self.root.after(0, self.display_frame)
            
            frame_count += 1
            
    def start_recognition(self):
        if not self.rectangles:
            messagebox.showwarning("警告", "请先添加矩形框")
            return
            
        if not self.cap:
            messagebox.showwarning("警告", "请先选择视频文件")
            return
        
        if self.is_recognizing:
            messagebox.showwarning("警告", "识别正在进行中")
            return
            
        # 开始识别线程
        self.is_recognizing = True
        threading.Thread(target=self.recognition_thread, daemon=True).start()
    
    def stop_recognition(self):
        """停止识别"""
        self.is_recognizing = False
        self.status_label.config(text="识别已停止")
        
    def recognition_thread(self):
        self.status_label.config(text="正在快速识别中...")
        
        # 重置结果
        self.recognition_results = []
        
        # 获取视频总帧数
        total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
        
        # 计算每秒对应的帧数
        frames_per_second = self.frame_rate
        
        # 计算总秒数
        total_seconds = int(total_frames / frames_per_second)
        
        print(f"=== 开始快速识别 ===")
        print(f"视频总帧数: {total_frames}")
        print(f"视频帧率: {frames_per_second} fps")
        print(f"预计处理秒数: {total_seconds}")
        print(f"识别区域数量: {len(self.rectangles)}")
        print("=" * 50)
        
        # 每秒只处理最后一帧，快速跳帧
        for second in range(0, total_seconds):
            # 检查是否停止识别
            if not self.is_recognizing:
                break
                
            # 计算这一秒的最后一帧索引
            frame_index = (second + 1) * frames_per_second - 1
            
            # 确保不超出视频范围
            if frame_index >= total_frames:
                frame_index = total_frames - 1
            
            # 跳转到指定帧
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
            ret, frame = self.cap.read()
            
            if not ret:
                continue
            
            # 更新当前帧显示（快速播放效果）
            self.current_frame = frame
            self.root.after(0, self.display_frame)
                
            # 处理每个矩形框
            frame_results = []
            print(f"\n--- 第 {second + 1} 秒 (帧 {frame_index}) ---")
            
            for i, rect in enumerate(self.rectangles):
                x1, y1, x2, y2, name = rect
                
                # 提取ROI
                roi = frame[int(y1):int(y2), int(x1):int(x2)]
                
                # 使用OCR识别文本
                recognized_text = self.recognize_number(roi)
                
                # 打印识别结果
                print(f"  区域 {i+1} [{name}]: {recognized_text}")
                
                frame_results.append({
                    'timestamp': second + 1,  # 使用秒数作为时间戳
                    'name': name,
                    'number': recognized_text,
                    'frame': frame_index
                })
                
            self.recognition_results.extend(frame_results)
            
            # 更新状态 - 显示快速处理进度
            progress = ((second + 1) / total_seconds) * 100
            self.root.after(0, lambda p=progress, s=second+1: self.status_label.config(text=f"快速识别进度: {p:.1f}% (第{s}秒)"))
            
            # 短暂延迟，让用户看到快速播放效果
            time.sleep(0.1)
        
        # 识别完成或停止后的处理
        print("\n" + "=" * 50)
        if self.is_recognizing:
            print("=== 快速识别完成 ===")
            self.root.after(0, lambda: self.status_label.config(text=f"快速识别完成！共处理 {len(self.recognition_results)} 个结果，用时约 {len(self.recognition_results) / len(self.rectangles):.1f} 秒"))
        else:
            print("=== 识别已停止 ===")
            self.root.after(0, lambda: self.status_label.config(text=f"识别已停止，共处理 {len(self.recognition_results)} 个结果"))
        
        # 打印详细统计信息
        print(f"总处理结果数: {len(self.recognition_results)}")
        print(f"识别区域数量: {len(self.rectangles)}")
        print(f"处理秒数: {len(self.recognition_results) // len(self.rectangles) if self.rectangles else 0}")
        
        # 按区域统计识别成功率
        if self.rectangles:
            print("\n--- 各区域识别统计 ---")
            for i, rect in enumerate(self.rectangles):
                name = rect[4]
                region_results = [r for r in self.recognition_results if r['name'] == name]
                success_count = len([r for r in region_results if r['number'] != "未识别" and r['number'] != "识别错误"])
                total_count = len(region_results)
                success_rate = (success_count / total_count * 100) if total_count > 0 else 0
                print(f"  区域 {i+1} [{name}]: {success_count}/{total_count} ({success_rate:.1f}%)")
        
        print("=" * 50)
        self.is_recognizing = False
        
    def recognize_number(self, roi):
        """识别ROI中的文本，支持多种OCR引擎"""
        try:
            # 优先使用 OpenCV OCR（最快）
            if not hasattr(self, 'opencv_ocr'):
                try:
                    from fast_opencv_ocr import FastOpenCVOCR
                    self.opencv_ocr = FastOpenCVOCR()
                except ImportError:
                    self.opencv_ocr = None
            
            if self.opencv_ocr:
                result = self.opencv_ocr.recognize_text(roi)
                if result and result != "未识别":
                    return result
            
            # 转换为灰度图
            gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
            
            # 预处理
            _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
            
            # 尝试使用 Tesseract OCR（较快）
            try:
                import pytesseract
                text = pytesseract.image_to_string(thresh, lang='eng', config='--psm 6')
                if text.strip():
                    return text.strip()
            except (ImportError, Exception):
                # Tesseract 未安装或出错，跳过
                pass
            
            # 尝试使用 PaddleOCR（较慢）
            try:
                from paddleocr import PaddleOCR
                if not hasattr(self, 'paddle_ocr'):
                    # 使用新的参数名称，避免过时警告
                    # 配置使用国内模型源
                    self.paddle_ocr = PaddleOCR(
                        use_textline_orientation=False, 
                        lang='en',
                        # 使用国内模型源
                        det_model_dir=None,  # 自动下载到国内源
                        rec_model_dir=None,  # 自动下载到国内源
                        cls_model_dir=None   # 自动下载到国内源
                    )
                
                # 智能处理不同版本的 PaddleOCR API
                results = None
                
                # 根据测试结果，优先使用 predict 方法
                if hasattr(self.paddle_ocr, 'predict'):
                    try:
                        results = self.paddle_ocr.predict(thresh)
                    except Exception:
                        pass
                
                # 如果 predict 失败，尝试其他方法
                if results is None:
                    try:
                        # 尝试不带参数的 ocr 方法
                        results = self.paddle_ocr.ocr(thresh)
                    except Exception:
                        pass
                
                # 处理结果
                if results and len(results) > 0:
                    # 新版本返回格式：[[(bbox, text, confidence), ...]]
                    if isinstance(results[0], list) and len(results[0]) > 0:
                        texts = []
                        for item in results[0]:
                            if len(item) >= 2 and isinstance(item[1], tuple) and len(item[1]) >= 1:
                                text = item[1][0]
                                confidence = item[1][1] if len(item[1]) > 1 else 1.0
                                if confidence > 0.5:
                                    texts.append(text.strip())
                        if texts:
                            return ' '.join(texts)
                    
                    # 旧版本返回格式：[(bbox, text, confidence), ...]
                    elif isinstance(results[0], tuple) and len(results[0]) >= 2:
                        texts = []
                        for item in results[0]:
                            if len(item) >= 2:
                                text = item[1]
                                confidence = item[2] if len(item) > 2 else 1.0
                                if confidence > 0.5:
                                    texts.append(text.strip())
                        if texts:
                            return ' '.join(texts)
            except ImportError:
                pass
            
            # 使用 EasyOCR（最慢但准确）
            try:
                import easyocr
                if not hasattr(self, 'easy_ocr_reader'):
                    self.easy_ocr_reader = easyocr.Reader(['en'])
                
                results = self.easy_ocr_reader.readtext(thresh)
                texts = []
                for (bbox, text, prob) in results:
                    if prob > 0.3:
                        texts.append(text.strip())
                if texts:
                    return ' '.join(texts)
            except ImportError:
                pass
            
            return "未识别"
            
        except Exception as e:
            return f"识别错误: {str(e)}"
            
    def export_csv(self):
        if not self.recognition_results:
            messagebox.showwarning("警告", "没有识别结果可导出")
            return
            
        file_path = filedialog.asksaveasfilename(
            title="导出CSV文件",
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                df = pd.DataFrame(self.recognition_results)
                df.to_csv(file_path, index=False, encoding='utf-8-sig')
                messagebox.showinfo("成功", f"已导出到: {file_path}")
            except Exception as e:
                messagebox.showerror("错误", f"导出失败: {str(e)}")
                
    def save_config(self):
        config = {
            'video_path': self.video_path,
            'rectangles': self.rectangles
        }
        
        file_path = filedialog.asksaveasfilename(
            title="保存配置文件",
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(config, f, ensure_ascii=False, indent=2)
                messagebox.showinfo("成功", f"配置已保存到: {file_path}")
            except Exception as e:
                messagebox.showerror("错误", f"保存失败: {str(e)}")
                
    def load_config(self):
        config_file = "config.json"
        if os.path.exists(config_file):
            try:
                with open(config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    
                if 'video_path' in config and os.path.exists(config['video_path']):
                    self.video_path = config['video_path']
                    self.load_video()
                    
                if 'rectangles' in config:
                    self.rectangles = config['rectangles']
                    
            except Exception as e:
                print(f"加载配置失败: {e}")
    
    def load_config_dialog(self):
        """载入配置文件对话框"""
        file_path = filedialog.askopenfilename(
            title="选择配置文件",
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                
                # 载入视频路径
                if 'video_path' in config and os.path.exists(config['video_path']):
                    self.video_path = config['video_path']
                    self.load_video()
                    messagebox.showinfo("成功", f"已载入视频: {self.video_path}")
                elif 'video_path' in config:
                    messagebox.showwarning("警告", f"视频文件不存在: {config['video_path']}")
                
                # 载入矩形框配置
                if 'rectangles' in config:
                    self.rectangles = config['rectangles']
                    self.selected_rect = None
                    self.display_frame()
                    messagebox.showinfo("成功", f"已载入 {len(self.rectangles)} 个矩形框")
                
            except Exception as e:
                messagebox.showerror("错误", f"载入配置失败: {str(e)}")
    
    def run(self):
        self.root.mainloop()
        
    def __del__(self):
        if self.cap:
            self.cap.release()

if __name__ == "__main__":
    app = VideoProcessor()
    app.run()
